package com.ease.component.controls {

   import flash.display.DisplayObject;
   import flash.events.Event;
   import flash.utils.Dictionary;

   import mx.containers.ViewStack;
   import mx.core.INavigatorContent;
   import mx.core.IUIComponent;
   import mx.events.IndexChangedEvent;

   [Event(name="DeferredChildAdd", type="flash.events.Event")]
   public class DeferredViewStack extends ViewStack {

      public function DeferredViewStack() {
         super();
         addEventListener("DeferredChildAdd", onViewStackContentChange);
      }

      private var insertIndices:Array = [];

      //---------------------
      // deferredChildren
      //---------------------
      private var _deferredChildren:Array = [];

      public function get deferredChildren():Array {
         return _deferredChildren;
      }

      //---------------------
      // deferredNumChildren
      //---------------------
      public function get deferredNumChildren():int {
         return deferredChildren.length;
      }

      //----------------
      // leadingPath
      //----------------
      /**
       * The leading url fragment path to this ViewStackWithHistory as INavigator.
       */
      private var _leadingPath:String = "";
      public function get leadingPath():String {
         return _leadingPath;
      }
      public function set leadingPath(value:String):void {
         _leadingPath = value;
      }

      //----------------
      // pathHistory
      //----------------
      /**
       * The cached path history for all paths under this INavigator node.
       */
      private var _pathHistory:Dictionary;
      public function get pathHistory():Dictionary {
         return _pathHistory;
      }
      public function set pathHistory(value:Dictionary):void {
         _pathHistory = value;
      }

      //----------------
      // navigationKey
      //----------------
      /**
       * The key for navigation on this INavigator node.
       */
      private var _navigationkey:String;
      public function get navigationKey():String {
         return _navigationkey;
      }
      public function set navigationKey(value:String):void {
         _navigationkey = value;
      }

      //----------------
      // navigatedValue
      //----------------
      /**
       * The value that is or will be navigated to on this INavigator node.
       */
      private var _navigatedValue:String;
      public function get navigatedValue():String {
         return _navigatedValue;
      }
      public function set navigatedValue(value:String):void {
         _navigatedValue = value;
      }

      //------------------------
      // deferredSelectedIndex
      //------------------------
      [Bindable]
      public function get deferredSelectedIndex():int {
         if (selectedChild != null) {
            var cindex:int = getChildIndex(selectedChild as DisplayObject);
            return insertIndices[cindex];
         } else {
            return -1;
         }
      }

      public function set deferredSelectedIndex(value:int):void {
         if (insertIndices.indexOf(value) == -1) {
            var index:int = updateInsertIndices(value);
            var oldSelectedIndex:int = selectedIndex;
            var indexChanged:Boolean = (index != selectedIndex);
            var child:DisplayObject = deferredChildren[value] as DisplayObject;

            if (!contains(child)) {
               // add child to view stack.
               if (index < numChildren) {
                  (getChildAt(index) as IUIComponent).setVisible(false);

                  super.addChildAt(child, index);
               } else {
                  super.addChild(child);
               }
            }

            selectedChild = child as INavigatorContent;

            if (!indexChanged) {
               var event:IndexChangedEvent = new IndexChangedEvent(IndexChangedEvent.CHANGE);
               event.newIndex = index;
               event.oldIndex = oldSelectedIndex;
               dispatchEvent(event);
            }
         } else {
            selectedChild = getChildAt(insertIndices.indexOf(value)) as INavigatorContent;
         }
      }

      //------------------------
      // deferredSelectedChild
      //------------------------
      public function set deferredSelectedChild(value:DisplayObject):void {
         deferredSelectedIndex = deferredChildren.indexOf(value);
      }


      public function getDeferredChildAt(index:int):DisplayObject {
         if (index < deferredNumChildren) {
            return deferredChildren[index];
         } else {
            return null;
         }
      }

      public function removeDeferredChildAt(index:int):DisplayObject {
         var child:DisplayObject = getDeferredChildAt(index);

         if (child != null) {
            var oldDefSelIndex:int = deferredSelectedIndex;

            deferredChildren.splice(index, 1);

            var len:int = insertIndices.length;
            if (insertIndices.indexOf(index) != -1) {

               var start:int = insertIndices.indexOf(index);
               for (var itr:int = start + 1; itr < len; itr++) {
                  insertIndices[itr] -= 1;
               }

               insertIndices.splice(insertIndices.indexOf(index), 1);
               removeChild(child);

               //update selected child.
               if (deferredNumChildren == 0) {
                  insertIndices = [];
               }
               else {
                  var newIndex:int = (oldDefSelIndex < deferredNumChildren) ? oldDefSelIndex : oldDefSelIndex - 1;
                  deferredSelectedIndex = (newIndex > 0) ? newIndex : 0;
   
                  if (insertIndices.length == 1 || deferredSelectedIndex == oldDefSelIndex) {
                     var event:IndexChangedEvent = new IndexChangedEvent(IndexChangedEvent.CHANGE);
                     event.newIndex = selectedIndex;
                     event.oldIndex = selectedIndex;
                     dispatchEvent(event);
                  }
               }
            } else {
               len = insertIndices.length;
               for (itr = 0; itr < len; itr++) {
                  if (insertIndices[itr] > index) {
                     insertIndices[itr] -= 1;
                  }
               }

               event = new IndexChangedEvent(IndexChangedEvent.CHANGE);
               event.newIndex = selectedIndex;
               event.oldIndex = selectedIndex;
               dispatchEvent(event);
            }
         }

         return child;
      }

      /**
       * @private
       */
      private function onViewStackContentChange(event:Event):void {
         if (!deferredNumChildren > 0) {
            if (deferredSelectedIndex == -1) {
               deferredSelectedIndex = 0;

               /*
               ViewStack will not dispatch IndexChangedEvent when selectedIndex changed from -1 to 0.
               To make right side panel start to initialize content, we should dispatch that event manually.
               */
               var evt:IndexChangedEvent = new IndexChangedEvent(IndexChangedEvent.CHANGE);
               evt.newIndex = 0;
               evt.oldIndex = 0;
               dispatchEvent(evt);
            }
            return;
         }
      }


      private function updateInsertIndices(value:int):int {
         var index:int;
         var len:int = insertIndices.length;

         if (len == 0) {
            insertIndices.push(value);
            index = 0;
         } else if (len == 1) {
            if (value <= insertIndices[0]) {
               insertIndices.unshift(value);
               index = 0;
            } else {
               insertIndices.push(value);
               index = 1;
            }
         } else {
            if (value <= insertIndices[0]) {
               insertIndices.unshift(value);
               index = 0;
            } else if (value > insertIndices[len - 1]) {
               insertIndices.push(value);
               index = len;
            } else {
               for (var fitr:int = 0; fitr < len - 1; fitr++) {
                  if (value > insertIndices[fitr] && value < insertIndices[fitr + 1]) {
                     insertIndices.splice(fitr + 1, 0, value);
                     index = fitr + 1;
                     break;
                  }
               }
            }
         }

         return index;
      }

      public function addDeferredChild(child:DisplayObject):DisplayObject {
         // deferred to initialize the child, note - do nothing here except
         // cache the children.
         deferredChildren.push(child);

         dispatchEvent(new Event("DeferredChildAdd"));

         return child;
      }
      
      override public function removeAllChildren():void {
         super.removeAllChildren();
         insertIndices = [];
         _deferredChildren = [];
      }
   }
}
